home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Freeware / Miro 1.0 / Miro_Installer.exe / xulrunner / python / playlist.py < prev    next >
Encoding:
Python Source  |  2007-11-12  |  8.3 KB  |  249 lines

  1. # Miro - an RSS based video player application
  2. # Copyright (C) 2005-2007 Participatory Culture Foundation
  3. #
  4. # This program is free software; you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation; either version 2 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program; if not, write to the Free Software
  16. # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  17.  
  18. """Miro playlist support."""
  19.  
  20. from gtcache import gettext as _
  21.  
  22. import app
  23. import dialogs
  24. import database
  25. import filters
  26. import menu
  27. import item
  28. import views
  29. import sorts
  30. from databasehelper import makeSimpleGetSet, TrackedIDList
  31.  
  32. class PlaylistMixin:
  33.     """Class that handles basic playlist functionality.  PlaylistMixin is used
  34.     by both SavedPlaylist and folder.PlaylistFolder.
  35.     """
  36.  
  37.     def setupTrackedItemView(self):
  38.         self.trackedItems = TrackedIDList(views.items, self.item_ids)
  39.         views.items.addRemoveCallback(self.onItemRemoved)
  40.  
  41.     def onItemRemoved(self, obj, id):
  42.         if id in self.trackedItems:
  43.             self.trackedItems.removeID(id)
  44.  
  45.     def getItems(self):
  46.         """Get the items in this playlist."""
  47.         self.confirmDBThread()
  48.         return [i for i in self.getView()]
  49.  
  50.     def getView(self):
  51.         return self.trackedItems.view
  52.  
  53.     def setSearch(self, searchTerms):
  54.         """Set a search to limit the items in this playlist.  
  55.  
  56.         NOTE: When this is called by the template code, it will change the
  57.         results of getView() and getItems().  I (BDK), feel like this is a
  58.         kind of ugly design, but I don't want to change things right now,
  59.         because that would involve extra sorts on the playlist view.  Right
  60.         now the only time we use getView() is in the template code, so I
  61.         didn't want to fix an issue that doesn't matter.
  62.         """
  63.  
  64.         def searchFilter(obj):
  65.             return filters.matchingItems(obj, searchTerms)
  66.         self.trackedItems.setFilter(searchFilter)
  67.     
  68.     def getFolder(self):
  69.         return None
  70.  
  71.     def addID(self, id):
  72.         """Add a new item to end of the playlist.  """
  73.         self.confirmDBThread()
  74.         item = views.items.getObjectByID(id)
  75.         item.save()
  76.         if id not in self.trackedItems:
  77.             self.trackedItems.appendID(id)
  78.  
  79.         folder = self.getFolder()
  80.         if (folder is not None):
  81.             folder.addID(id)
  82.  
  83.         self.signalChange()
  84.  
  85.     def removeID(self, id):
  86.         """Remove an item from the playlist."""
  87.  
  88.         self.confirmDBThread()
  89.         self.trackedItems.removeID(id)
  90.  
  91.         folder = self.getFolder()
  92.         if (folder is not None):
  93.             folder.addID(id)
  94.  
  95.         self.signalChange()
  96.  
  97.     def moveID(self, id, newPosition):
  98.         """Change the position of an item in the playlist.
  99.  
  100.         This method works the same as list.insert().  The item will be
  101.         inserted at the index newPosition, items that are currently at that
  102.         index or after it will be moved back one position.  If new position is
  103.         after the end of the list, the item will be added at the end, if it's
  104.         less than 0 it will be added at the begining.
  105.         """
  106.  
  107.         self.confirmDBThread()
  108.         self.trackedItems.moveID(id, newPosition)
  109.         self.signalChange()
  110.  
  111.     def addItem(self, item):
  112.         return self.addID(item.getID())
  113.  
  114.     def removeItem(self, item):
  115.         return self.removeID(item.getID())
  116.  
  117.     def moveItem(self, item, newPosition):
  118.         return self.moveID(item.getID(), newPosition)
  119.  
  120.     def handleDNDAppend(self, draggedIDs):
  121.         for id in draggedIDs:
  122.             if not views.items.idExists(id):
  123.                 raise KeyError("%s is not an item id" % id)
  124.             item = views.items.getObjectByID(id)
  125.             if not item.isContainerItem:
  126.                 self.addID(id)
  127.             else:
  128.                 for child in item.getChildren():
  129.                     self.addID(child.getID())
  130.  
  131.     def handleDNDReorder(self, anchorItem, draggedItems):
  132.         """Handle drag-and-drop reordering of the playlist."""
  133.         for iid in draggedItems:
  134.             if iid not in self.trackedItems:
  135.                 raise ValueError("id not in playlist folder: %s", iid)
  136.         if anchorItem is not None:
  137.             self.trackedItems.moveIDList(draggedItems, anchorItem.getID())
  138.         else:
  139.             self.trackedItems.moveIDList(draggedItems, None)
  140.         self.signalChange()
  141.         
  142.     def recomputeSort(self):
  143.         self.trackedItems.recomputeSort()
  144.  
  145. class SavedPlaylist(database.DDBObject, PlaylistMixin):
  146.     """An ordered list of videos that the user has saved.
  147.  
  148.     This class is called SavedPlaylist to distinguish it from app.Playlist,
  149.     which is a temporary playlist that holds the videos we're playing right
  150.     now.
  151.     """
  152.  
  153.     def __init__(self, title, items=None):
  154.         self.title = title
  155.         if items is not None:
  156.             self.item_ids = [i.getID() for i in items]
  157.         else:
  158.             self.item_ids = []
  159.         self.folder_id = None
  160.         self.setupTrackedItemView()
  161.         database.DDBObject.__init__(self)
  162.  
  163.     def onRestore(self):
  164.         self.setupTrackedItemView()
  165.  
  166.     getTitle, setTitle = makeSimpleGetSet('title')
  167.  
  168.     def getFolder(self):
  169.         self.confirmDBThread()
  170.         if self.folder_id is not None:
  171.             return self.dd.getObjectByID(self.folder_id)
  172.         else:
  173.             return None
  174.  
  175.     def setFolder(self, newFolder):
  176.         self.confirmDBThread()
  177.         old_folder_id = self.folder_id
  178.         if newFolder is not None:
  179.             self.folder_id = newFolder.getID()
  180.         else:
  181.             self.folder_id = None
  182.         self.signalChange()
  183.         if old_folder_id is not None:
  184.             folder = views.playlistFolders.getObjectByID(old_folder_id)
  185.             for id in self.item_ids:
  186.                 folder.checkItemIDRemoved(id)
  187.  
  188.     def handleRemove(self, ids):
  189.         """Handle the user removing a set of IDs.  This method will also check
  190.         the playlist folder we're in and remove the ID from there.
  191.         """
  192.  
  193.         for id in ids:
  194.             self.removeID(id)
  195.         folder = self.getFolder()
  196.         if folder:
  197.             for id in ids:
  198.                 folder.checkItemIDRemoved(id)
  199.  
  200.     def getDragDestType(self):
  201.         self.confirmDBThread()
  202.         if self.folder_id is not None:
  203.             return 'playlist'
  204.         else:
  205.             return 'playlist:playlistfolder'
  206.  
  207.     def makeContextMenu(self, templateName, view):
  208.         return menu.makeMenu([
  209.             (self.rename, _('Rename Playlist')),
  210.             (lambda: app.controller.removePlaylist(self), _('Remove')),
  211.         ])
  212.  
  213.     def rename(self):
  214.         title = _("Rename Playlist")
  215.         description = _("Enter a new name for the playlist %s" % self.getTitle())
  216.  
  217.         def callback(dialog):
  218.             if self.idExists() and dialog.choice == dialogs.BUTTON_OK:
  219.                 self.setTitle(dialog.value)
  220.         dialogs.TextEntryDialog(title, description, dialogs.BUTTON_OK,
  221.                 dialogs.BUTTON_CANCEL).run(callback)
  222.  
  223.     # This allows playlists to be used in the same context as folders
  224.     # in certain places, but still catches logic problem. Maybe
  225.     # eventually, playlists and folders should derive from the same
  226.     # parent --NN
  227.     def remove(self,moveItemsTo=None):
  228.         if moveItemsTo is not None:
  229.             raise StandardError("Cannot 'move' a playlist to %s" % repr(moveItemsTo))
  230.         database.DDBObject.remove(self)
  231.  
  232. def createNewPlaylist(childIDs=None):
  233.     """Start the new playlist creation process.  This should be called in
  234.     response to the user clicking on the new playlist menu option.
  235.     """
  236.  
  237.     title = _("Create Playlist")
  238.     description = _("Enter a name for the new playlist")
  239.  
  240.     def callback(dialog):
  241.         if dialog.choice == dialogs.BUTTON_CREATE:
  242.             playlist = SavedPlaylist(dialog.value)
  243.             app.controller.selection.selectTabByObject(playlist)
  244.             if childIDs:
  245.                 playlist.handleDNDAppend(childIDs)
  246.  
  247.     dialogs.TextEntryDialog(title, description, dialogs.BUTTON_CREATE,
  248.             dialogs.BUTTON_CANCEL).run(callback)
  249.